//===============================================================================
// Microsoft patterns & practices Enterprise Library Contribution
// Resource Application Block
//===============================================================================

using System;
using System.Diagnostics;
using Microsoft.Practices.EnterpriseLibrary.Common.Instrumentation;

namespace EntLibContrib.Resource.Instrumentation
{
	/// <summary>
	/// Provides the concrete instrumentation for the logical events raised by a <see cref="ResourceInstrumentationProvider"/> object.
	/// </summary>
	[HasInstallableResources]
	[PerformanceCountersDefinition(ResourceCountersCategoryName, "ResourceCountersCategoryHelpResource")]
	[EventLogDefinition("Application", EventLogSourceName)]
	public class ResourceInstrumentationListener : InstrumentationListener
	{
		#region Constants
		internal const string EventLogSourceName = "Enterprise Library Resource";
		internal const string ResourceCountersCategoryName = "Enterprise Library Resource Counters";
		internal const string ResourceEntriesCounterName = "Total Resource Entries";
		internal const string ResourceAccessesCounterName = "Total Resource Accesses";
		internal const string ResourceAccessRateCounterName = "Resource Access Rate";
		#endregion

		#region Static Fields
		static EnterpriseLibraryPerformanceCounterFactory factory = new EnterpriseLibraryPerformanceCounterFactory();
		#endregion

		#region Fields
		private string instanceName;
		private string counterInstanceName;
		private IEventLogEntryFormatter eventLogEntryFormatter;
		#endregion

		#region Counters
		[PerformanceCounter(ResourceEntriesCounterName, "ResourceEntriesCounterHelpResource", PerformanceCounterType.NumberOfItems64)]
		EnterpriseLibraryPerformanceCounter resourceEntriesCounter;

		[PerformanceCounter(ResourceAccessesCounterName, "ResourceAccessesCounterHelpResource", PerformanceCounterType.NumberOfItems64)]
		EnterpriseLibraryPerformanceCounter resourceAccessCounter;

		[PerformanceCounter(ResourceAccessRateCounterName, "ResourceAccessRateCounterHelpResource", PerformanceCounterType.RateOfCountsPerSecond32)]
		EnterpriseLibraryPerformanceCounter resourceAccessRateCounter;
		#endregion

		#region Construction
		/// <summary>
		/// Initializes a new instance of the <see cref="ResourceInstrumentationListener"/> class.
		/// </summary>
		/// <param name="instanceName">The name of the <see cref="ResourceManager"/> instance this instrumentation listener is created for.</param>
		/// <param name="performanceCountersEnabled"><b>true</b> if performance counters should be updated.</param>
		/// <param name="eventLoggingEnabled"><b>true</b> if event log entries should be written.</param>
		/// <param name="wmiEnabled"><b>true</b> if WMI events should be fired.</param>
		public ResourceInstrumentationListener(string instanceName, bool performanceCountersEnabled, bool eventLoggingEnabled, bool wmiEnabled)
			: this(instanceName, performanceCountersEnabled, eventLoggingEnabled, wmiEnabled, new AppDomainNameFormatter())
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="ResourceInstrumentationListener"/> class.
		/// </summary>
		/// <param name="instanceName">The name of the <see cref="ResourceManager"/> instance this instrumentation listener is created for.</param>
		/// <param name="performanceCountersEnabled"><b>true</b> if performance counters should be updated.</param>
		/// <param name="eventLoggingEnabled"><b>true</b> if event log entries should be written.</param>
		/// <param name="wmiEnabled"><b>true</b> if WMI events should be fired.</param>
		/// <param name="nameFormatter">The <see cref="IPerformanceCounterNameFormatter"/> that is used to create a unique name for each <see cref="PerformanceCounter"/> instance.</param>
		public ResourceInstrumentationListener(string instanceName, bool performanceCountersEnabled, bool eventLoggingEnabled, bool wmiEnabled, IPerformanceCounterNameFormatter nameFormatter)
			: base(new string[] { instanceName }, performanceCountersEnabled, eventLoggingEnabled, wmiEnabled, nameFormatter)
		{
			this.instanceName = instanceName;
			this.counterInstanceName = CreateInstanceName(instanceName);
			if (EventLoggingEnabled)
				this.eventLogEntryFormatter = new EventLogEntryFormatter(InternalResources.BlockName);
		}
		#endregion

		#region Public Methods
		/// <summary>
		/// This method supports the Enterprise Library infrastructure and is not intended to be used directly from your code.
		/// Handler for the <see cref="ResourceInstrumentationProvider.ResourceInitialized"/> event.
		/// </summary>
		/// <param name="sender">Sender of the event.</param>
		/// <param name="e">Data for the event.</param>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers"), InstrumentationConsumer("ResourceInitialized")]
		public void ResourceInitialized(object sender, ResourceInitializedEventArgs e)
		{
			if (e == null)
				throw new ArgumentNullException("e");

			if (PerformanceCountersEnabled)
				resourceEntriesCounter.SetValueFor(counterInstanceName, e.TotalEntriesCount);
		}

		/// <summary>
		/// This method supports the Enterprise Library infrastructure and is not intended to be used directly from your code.
		/// Handler for the <see cref="ResourceInstrumentationProvider.ResourceAccessed"/> event.
		/// </summary>
		/// <param name="sender">Sender of the event.</param>
		/// <param name="e">Data for the event.</param>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers"), InstrumentationConsumer("ResourceAccessed")]
		public void ResourceAccessed(object sender, ResourceAccessedEventArgs e)
		{
			if ( PerformanceCountersEnabled )
			{
				resourceAccessCounter.Increment();
				resourceAccessRateCounter.Increment();
			}
		}

		/// <summary>
		/// This method supports the Enterprise Library infrastructure and is not intended to be used directly from your code.
		/// Handler for the <see cref="ResourceInstrumentationProvider.ResourceFailed"/> event.
		/// </summary>
		/// <param name="sender">Sender of the event.</param>
		/// <param name="e">Data for the event.</param>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers"), InstrumentationConsumer("ResourceFailed")]
		public void ResourceFailed(object sender, ResourceFailureEventArgs e)
		{
			if (e == null)
				throw new ArgumentNullException("e");
				
			if (WmiEnabled)
				FireManagementInstrumentation(new ResourceFailureEvent(instanceName, e.ErrorMessage, e.Exception.ToString()));

			if (EventLoggingEnabled)
			{
				string errorMessage = string.Format(InternalResources.Culture, InternalResources.ErrorResourceOperationFailedMessage, instanceName);
				string entryText = eventLogEntryFormatter.GetEntryText(errorMessage, e.Exception, e.ErrorMessage);

				EventLog.WriteEntry(GetEventSourceName(), entryText, EventLogEntryType.Error);
			}
		}
		#endregion

		#region Protected Methods
		/// <summary>
		/// Creates the performance counters to instrument the resource events for the specified instance names.
		/// </summary>
		/// <param name="instanceNames">The instance names for the performance counters.</param>
		protected override void CreatePerformanceCounters(string[] instanceNames)
		{
			resourceEntriesCounter = factory.CreateCounter(ResourceCountersCategoryName, ResourceEntriesCounterName, instanceNames);
			resourceAccessCounter = factory.CreateCounter(ResourceCountersCategoryName, ResourceAccessesCounterName, instanceNames);
			resourceAccessRateCounter = factory.CreateCounter(ResourceCountersCategoryName, ResourceAccessRateCounterName, instanceNames);
		}
		#endregion
	}
}